/**
 * Contains main class for testing
 */
package logic;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import logic.pathelements.Cable;
import logic.pathelements.Firewall;
import logic.pathelements.Hub;
import logic.pathelements.PC;
import logic.pathelements.PathElement;
import logic.pathelements.Ports;
import logic.pathelements.Protocol;
import logic.pathelements.Record;
import logic.pathelements.Router;
import logic.pathelements.RouterRecord;
import logic.pathelements.Switch;
import logic.pathelements.SwitchRecord;

/* 
add router 0 rt 2
add switch 1 sw 2
add cable 2 3 5
connect 0 0 2 0
connect 1 0 2 1
addrtrule 0 0 1.1.1.1 255.255.255.0 1.1.1.2 23
addrtrule 0 1 1.1.1.2 255.255.255.0 1.1.1.3 29
delrtrule 0 0 1.1.1.1 255.255.255.0 1.1.1.2 23
addswrule 1 0 34
delswrule 1 0 34
delswrule 1 0 34
addswrule 1 1 345
findroute delay IP 192.168.1.1 192.168.1.2 1234 1234
*/


/**
 *
 * @author
 */
public class NetworkModelTest {
	private static Network network;
    
    /**
     * @param args the command line arguments
     * @throws Exception 
     * @throws NumberFormatException some exception about error in array index
     * @throws ParseException 
     */
    public static void main(String[] args) throws NumberFormatException, Exception {
        String str = "";
        Scanner input = new Scanner(System.in);
        network = new Network();
        PathElement pathElement;
        OldNetworkModelTest.createNetwork(network);
        AkimovsRouteProvider akimovsRouteProvider = new AkimovsRouteProvider();
        while (true){
            str = input.nextLine(); // write string from console to string "str"
            //System.out.print(str); 
            try {
                    String[] parseStr= str.split(" "); // parsing string by space(" ")
                    switch(parseStr[0].toLowerCase()) {
                        // switching by command 
                        case "add":{ addParse(parseStr,network); break;}
                        case "del":{break;}
                        case "connect":{network.connect((Integer)Integer.parseInt(parseStr[1]),
                            Integer.parseInt(parseStr[2]), 
                            Integer.parseInt(parseStr[3]), 
                            Integer.parseInt(parseStr[4])); break;}
                        case "disconnect":{ network.disconnect((Integer)Integer.parseInt(parseStr[1]),
                            Integer.parseInt(parseStr[2])); break;}
                        case "setip":{ break;}
                    	case "addfwrule":
                    		// <command> <firewallId> <rule>
                    		addFwRule(Integer.parseInt(parseStr[1]),parseStr[2]); 
                    		break;
						case "delfwrule":
							//<command> <firewallId> <ruleId>
							delFwRule(Integer.parseInt(parseStr[1]),
									  Integer.parseInt(parseStr[2])); 
							break;
						case "showrulelist":
							//<command> <firewallId>
							showRuleList(Integer.parseInt(parseStr[1]));
							break;
                        case "addrtrule":
                            //addrtrule routerid connectorNumber  destination, netmask gateway metric
                            if (7 != parseStr.length) {
                                System.out.println("Invalid addrtrule comand format. It must be: \n addrtrule routerid connectorNumber  destination, netmask gateway metric.");
                                break;
                            }
                            pathElement = network.getPathElement((Integer)Integer.parseInt(parseStr[1]));
                            if (pathElement == null) {
                                 System.out.println("Invalid path element id");
                                 break;
                            }
                            if (!(pathElement instanceof Router)) {
                                System.out.println("The element identified by id is not router");
                                break;
                             }
                            ((Router) pathElement).addRecord(new RouterRecord(pathElement.getConnector(Integer.parseInt(parseStr[2])), parseStr[3], parseStr[4], parseStr[5], Integer.parseInt(parseStr[6])));
                            break;
                        case "delrtrule":
                            //delrtrule routerid connectorNumber  destination, netmask gateway metric
                            if (7 != parseStr.length) {
                                System.out.println("Invalid delrtrule comand format. It must be: \n delrtrule routerid connectorNumber  destination, netmask gateway metric.");
                                break;
                            }
                            pathElement = network.getPathElement((Integer)Integer.parseInt(parseStr[1]));
                            if (pathElement == null) {
                                 System.out.println("Invalid path element id");
                                 break;
                            }
                            if (!(pathElement instanceof Router)) {
                                System.out.println("The element identified by id is not router");
                                break;
                            }
                            ((Router) pathElement).removeRecord(new RouterRecord(pathElement.getConnector(Integer.parseInt(parseStr[2])), parseStr[3], parseStr[4], parseStr[5], Integer.parseInt(parseStr[6])));
                            break;
                        case "addswrule":
                            //addswrule routerid connectorNumber  destination, netmask gateway metric
                            if (4 != parseStr.length) {
                                System.out.println("Invalid addswrule comand format. It must be: \n addrtrule routerid connectorNumber  MAC.");
                                break;
                            }
                            pathElement = network.getPathElement((Integer)Integer.parseInt(parseStr[1]));
                            if (pathElement == null) {
                                 System.out.println("Invalid path element id");
                                 break;
                            }
                            if (!(pathElement instanceof Switch)) {
                                System.out.println("The element identified by id is not switch");
                                break;
                            }
                            ((Switch) pathElement).addRecord(new SwitchRecord(pathElement.getConnector(Integer.parseInt(parseStr[2])), Integer.parseInt(parseStr[3])));
                            break;
                        case "delswrule":
                            if (4 != parseStr.length) {
                                System.out.println("Invalid delswrule comand format. It must be: \n addrtrule routerid connectorNumber  MAC.");
                                break;
                            }
                            pathElement = network.getPathElement(Integer.parseInt(parseStr[1]));
                            if (pathElement == null) {
                                 System.out.println("Invalid path element id");
                                 break;
                            }
                            if (!(pathElement instanceof Switch)) {
                                System.out.println("The element identified by id is not switch");
                                break;
                            }
                            ((Switch) pathElement).removeRecord(new SwitchRecord(pathElement.getConnector(Integer.parseInt(parseStr[2])), Integer.parseInt(parseStr[3])));
                            break;
                        case "findroute":
                            if (7 != parseStr.length) {
                                System.out.println("Invalid findroute comand format. It must be: \n findroute (type: cost or delay) protocol srcip destip srcport destport");
                                break;
                            }
                            switch (parseStr[1].toLowerCase()) {
                                case "cost":
                                    System.out.println(akimovsRouteProvider.getRouteByCost(Protocol.valueOf(parseStr[2]), parseStr[3], parseStr[4], Integer.parseInt(parseStr[5]), Integer.parseInt(parseStr[6]), network));
                                    break;
                                case "delay":
                                    System.out.println(akimovsRouteProvider.getRouteByDelay(Protocol.valueOf(parseStr[2]), parseStr[3], parseStr[4], Integer.parseInt(parseStr[5]), Integer.parseInt(parseStr[6]), network));
                                    break;
                                default:
                                    System.out.println("Invalid type value. It must be cost or delay.");
                                    break;
                            }
                            break;
                        case "save":
                        	save(parseStr[1]); 
                        	break;
						case "restore":
							restore(parseStr[1]); 
							break;
                        case "networkdesc":
                            System.out.println(network.toString());
                            break;
                        case "exit" : {System.out.println("Now closing the terminal.."); return;}
                        default : {System.out.println("Please, write normal command"); break;}
                    }
            }
            catch (UnknownHostException e) {
                System.out.println("Invalid ip or host name");
            }
            catch(IllegalArgumentException e) {
                System.out.println(e.getMessage());
            }
            catch(ParseException e){e.printStackTrace();}
            catch (ArrayIndexOutOfBoundsException e){e.printStackTrace();}
        }
    }
	
    /**
     * @author Alexandr Shilov <alex.shilov92>
     * One of constructor are must be initialize with throws exception and NumberFormatException.\
     * Thats they are included in throws declaration.
     * @param Arraystring input param with - already parsing string - choise type of devise
     * @throws Exception 
     * @throws NumberFormatException 
     */
    static void addParse(String[] Arraystring, Network network) throws NumberFormatException, Exception
    {
            switch (Arraystring[1].toLowerCase())
            {
                    case "cable" : {network.addPathElement(new Cable((Integer)Integer.parseInt(Arraystring[2]),
                                                                                                                     Integer.parseInt(Arraystring[3]), 
                                                                                                             Integer.parseInt(Arraystring[4]))); break;}	
                    case "hub" : {network.addPathElement(new Hub((Integer)Integer.parseInt(Arraystring[2]),
                                                                                                             Integer.parseInt(Arraystring[3]),
                                                                                                             Integer.parseInt(Arraystring[4]),
                                                                                                             Integer.parseInt(Arraystring[5]))); break; }
                    case "switch" : {network.addPathElement(new Switch( (Integer)Integer.parseInt(Arraystring[2]),
                                                                                                                                             Arraystring[3],
                                                                                                                                             Integer.parseInt(Arraystring[4])));break; }
                    case "router" : {network.addPathElement(new Router( (Integer)Integer.parseInt(Arraystring[2]),
                                                                                                                                             Arraystring[3],
                                                                                                                                             Integer.parseInt(Arraystring[4]))); break; }
                    case "firewall" : {network.addPathElement(new Firewall((Integer)Integer.parseInt(Arraystring[2]),
                                                                                                                                                    Arraystring[3])); break; }
                    case "pc" : {network.addPathElement(new PC((Integer)Integer.parseInt(Arraystring[2]),
                                                                                                                            Arraystring[3],
                                                                                                                            (Integer.parseInt(Arraystring[4])))); break; }
                    default : {  System.out.println("Please, write normal name of add-don object");break;}
            }
    }
    
    /**
     * @author Dmitry Kuznetsov <zendipy@gmail.com>
     * Add new rule to firewall.
     * @param fwId 	ID of the firewall
     * @param rule	string in format "name|protocol|srcPort|destPort|srcIp|destIp|action"
     * available protocols: TCP, IP, UDP, ICMP
     * available actions: ACCEPT, DENY
     * @throws InvalidAttributeValueException
     */
    static private void addFwRule(int fwId, String rule) throws IllegalArgumentException{
    	Protocol protocol = null;
    	byte action = 0;
    	
    	String[] r = rule.split("|");
    	
    	// get protocol type
    	if(r[1].equals("TCP")){
    		protocol = Protocol.TCP;
    	}else if(r[1].equals("UDP")){
    		protocol = Protocol.UDP;
    	}else if(r[1].equals("IP")){
    		protocol = Protocol.IP;
    	}else if(r[1].equals("ICMP")){
    		protocol = Protocol.ICMP;
    	} else throw new IllegalArgumentException("Please input correct rule!");
    	
    	// get action
    	//1 - accept, 0 - deny
    	if(r[6].equals("ACCEPT")){
    		action = 1;
    	}else if(r[6].equals("DENY")){
    		action = 0;
    	} else throw new IllegalArgumentException("Please input correct rule!");
    		
    		
    	Firewall fw = null;
    	//fw.addNewRule(name, protocol, srcPort, destPort, srcIp, destIp, action);
    	/*for(PathElement p: network){
    		if(p instanceof Firewall){
    			fw = (Firewall) p;
    			break;
    		}
    	}*/
    	fw = (Firewall) network.getPathElement(fwId);
    	
    	
    	try {
    		fw.addNewRule(r[0], protocol, Integer.parseInt(r[2]), 
    					  Integer.parseInt(r[3]), InetAddress.getByName(r[4]), 
    					  InetAddress.getByName(r[5]), action);
    		System.out.println("The rule successfully added :)");
    	} catch (NumberFormatException e) {
    		e.printStackTrace();
    	} catch (UnknownHostException e) {
    		e.printStackTrace();
    	}
    	
    }

    /**
     * @author Dmitry Kuznetsov <zendipy@gmail.com>
     * Output list of firewall rules.
     * @param fwId 		ID of the firewall
     */
    static private void showRuleList(int fwId){
    	Firewall fw = null;
    	//fw.addNewRule(name, protocol, srcPort, destPort, srcIp, destIp, action);
    	/*for(PathElement p: network){
    		if(p instanceof Firewall){
    			fw = (Firewall) p;
    			break;
    		}
    	}*/
    	fw = (Firewall)network.getPathElement(fwId);
    	
    	System.out.println("------------ RULE LIST ---------------");
    	String[] rules = fw.getRuleList();
    	for(String r: rules){
    		System.out.println(r);
    	}
    	System.out.println("--------------- END ------------------");
    }

    /**
     * @author Dmitry Kuznetsov <zendipy@gmail.com>
     * Remove rule from firewall by it's ID.
     * @param ruleId	number of rule ID.
     * @param fwId 		ID of the firewall
     */
    static private void delFwRule(int fwId,int ruleId){
    	Firewall fw = null;
    	/*for(PathElement p: network){
    		if(p instanceof Firewall){
    			fw = (Firewall) p;
    			break;
    		}
    	}*/
    	fw = (Firewall)network.getPathElement(fwId);
    	
    	try{
    		fw.removeRule(ruleId);
    		System.out.println("The rule successfully removed :)");
    	}
    	catch(IllegalArgumentException e){
    		System.out.println("Error! No rule with such ID. :(");
    	}
    }

    /**
     * @author Dmitry Kuznetsov <zendipy@gmail.com>
     * Save {@link Network} to file.
     * @param fileName name of file
     */
    static private void save(String fileName){
    	System.out.print("API serialization.\n");
        Serializator sr = new Serializator();
        try {
    		sr.save(network, fileName);
    	} catch (IOException e) {
    		e.printStackTrace();
    	}
        System.out.println("Network has been saved to '"+fileName+"'.");
    }

    /**
     * @author Dmitry Kuznetsov <zendipy@gmail.com>
     * Load {@link Network} from file
     * @param fileName	name of file
     */
    static private void restore(String fileName){
    	Serializator sr = new Serializator();
        try {
    		network = (Network)sr.load(fileName);
    		System.out.println("Network has been loaded from '" + fileName+"'");
    	} catch (IOException e) {
    		e.printStackTrace();
    	} catch (ClassNotFoundException e) {
    		e.printStackTrace();
    	}
    }
}
